﻿#region License
/*
Copyright (c) 2011, André N. Klingsheim
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
 
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
*/
#endregion

using System;
using System.Reflection;
using System.Resources;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using TransformTool.Transformers;
using System.IO;
using TransformTool.Transformers.Panels;

namespace TransformTool
{
    public partial class TransformTool : Form
    {
        enum nodeStatus { Ok = 0, Error = 1, Warning = 2 }
        const int nodeTextLength = 20;
        bool textBoxKeyPressWasHandled = false;
        bool formKeyPressWasHandled = false;

        public TransformTool()
        {
            InitializeComponent();

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            TreeNode tn = new TreeNode("Input: ");
            tn.Tag = "";
            treeView1.Nodes.Add(tn);
            treeView1.SelectedNode = tn;
            tn.Text = "Add input in textbox ->";
            textBoxInputOutput.SelectionStart = 0;
            textBoxInputOutput.SelectionLength = textBoxInputOutput.TextLength;

            toolStripStatusLabel1.Alignment = ToolStripItemAlignment.Left;
            toolStripStatusLabelPosition.Alignment = ToolStripItemAlignment.Right;
            toolStripStatusLabelSelected.Alignment = ToolStripItemAlignment.Right;

            UpdateTextboxCursorPositionBySelection();
            //tn.SelectedImageIndex = 1000;

            initTransformerListboxes();
            this.treeViewStringTransformers.MouseDoubleClick += new MouseEventHandler(TreeviewTransformers_MouseDoubleClick);
            this.treeViewByteTransformers.MouseDoubleClick += new MouseEventHandler(TreeviewTransformers_MouseDoubleClick);

            this.treeViewStringTransformers.KeyDown += new KeyEventHandler(TreeViewTransformers_KeyDown);
            this.treeViewByteTransformers.KeyDown += new KeyEventHandler(TreeViewTransformers_KeyDown);

            this.treeViewByteTransformers.Enabled = false;

            var asm = this.GetType().Assembly;
            // Creates the ResourceManager.
            var resourceManager = new ResourceManager("TransformTool.Properties.Resources", asm);
            this.Invalidate();

        }

        void TreeviewTransformers_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            var theTreeview = (TreeView)sender;
            var clickedNode = theTreeview.GetNodeAt(e.Location);

            if (!(clickedNode.Tag is BaseTransformer))
                return;


            if (!ClickedTreeviewTransformerIsApplicable(clickedNode))
                return;

            AddTreeNodeFromTreeviewTransformer((BaseTransformer)clickedNode.Tag);

        }

        private void AddTreeNodeFromTreeviewTransformer(BaseTransformer transformer)
        {
            AddTreeNodeFromTreeviewTransformer(transformer, true);
        }

        private void AddTreeNodeFromTreeviewTransformer(BaseTransformer transformer, bool selectNewNode)
        {

            var newTransformerInstance = TransformerHelper.GetNewTransformerInstance(transformer);
            var newNode = new TreeNode()
            {

                Text = newTransformerInstance.NameForMenu,
                Tag = newTransformerInstance
            };

            treeView1.SelectedNode.Nodes.Add(newNode);
            if (selectNewNode)
                treeView1.SelectedNode = newNode;

            newNode.EnsureVisible();
            recalculateOutput(newNode);
            SubscribeToTransformerConfigUpdates(newTransformerInstance);
        }

        private void SubscribeToTransformerConfigUpdates(BaseTransformer transformer)
        {
            var configurableTransformer = transformer as BaseConfigurableTransformer;

            if (configurableTransformer != null)
                configurableTransformer.Config.ConfigChanged += new EventHandler(ConfigurableTransformer_ConfigChanged);
        }

        void ConfigurableTransformer_ConfigChanged(object sender, EventArgs e)
        {
            recalculateOutput(treeView1.SelectedNode);
            updateDescendingNodesStatuses(treeView1.SelectedNode);
        }

        bool ClickedTreeviewTransformerIsApplicable(TreeNode node)
        {
            if (!(node.Tag is BaseTransformer))
                return false;
            var transformer = (BaseTransformer)node.Tag;

            if (!getSelectedNodeOutputType().Equals(transformer.InputType))
                return false;

            return true;
        }

        private void treeView1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                if (treeView1.Nodes.Count > 0)
                    deleteSelectedNodeAndUpdateSelection();

                if (treeView1.Nodes.Count == 0)
                {
                    var newNode = treeView1.Nodes.Add("Input:");
                    newNode.Tag = "";
                    treeView1.SelectedNode = newNode;
                }
            }
        }

        private void deleteSelectedNodeAndUpdateSelection()
        {
            var selectedNode = treeView1.SelectedNode;

            if (selectedNode.NextNode != null)
                treeView1.SelectedNode = selectedNode.NextNode;
            else if (selectedNode.PrevNode != null)
                treeView1.SelectedNode = selectedNode.PrevNode;
            else if (selectedNode.Parent != null)
                treeView1.SelectedNode = selectedNode.Parent;

            treeView1.Nodes.Remove(selectedNode);
        }

        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            UpdateConfigPanel();
            var selectedNode = e.Node;
            if (selectedNode.Level == 0)
            {
                doInputUpdate(selectedNode);
            }
            else
            {
                recalculateOutput(selectedNode);
            }
            ResetTextboxCursorPosition();
            selectedNode.EnsureVisible();

        }

        private void UpdateConfigPanel()
        {
            var transformer = treeView1.SelectedNode.Tag as BaseConfigurableTransformer;

            if (transformer != null)
            {
                // We have config!
                splitContainerInputConfig.Panel2.Controls.Add(PanelHelper.GetPanelForConfig(transformer.Config));
            }
            else
            {
                splitContainerInputConfig.Panel2.Controls.Clear();
            }
            splitContainerInputConfig.Refresh();
        }


        private void doInputUpdate(TreeNode node)
        {
            if (node.Tag is string)
                doTextInputUpdate(node);
            else
                doFileInputUpdate(node);
        }
        private void doFileInputUpdate(TreeNode node)
        {

            textBoxInputOutput.Text = (string)new HEXEncodeTransformer().Transform(node.Tag);
            treeViewStringTransformers.Enabled = false;
            treeViewByteTransformers.Enabled = true;
            setStatusBarByteLength(((byte[])node.Tag).Length);
        }
        private void doTextInputUpdate(TreeNode node)
        {

            var tag = (string)node.Tag;
            if (tag != null)
            {
                //labelInputOutput.Text = "Input:";
                textBoxInputOutput.Text = "";
                textBoxInputOutput.Text = tag;
            }

            foreach (TreeNode childNode in node.Nodes)
                recalculateOutput(childNode);

            treeViewStringTransformers.Enabled = true;
            treeViewByteTransformers.Enabled = false;
            setStatusBarStringLength(textBoxInputOutput.TextLength);
        }

        //When someting changes on a node, output changes for all descending nodes.
        private void recalculateOutput(TreeNode theNode)
        {

            var tag = (BaseTransformer)theNode.Tag;
            //labelInputOutput.Text = tag.nameForMenu;

            var transforms = new Stack<TreeNode>();
            var node = theNode;
            while (node.Level > 0)
            {
                transforms.Push(node);
                node = node.Parent;
            }
            // Node is now root node

            var value = node.Tag;

            Type lastOutputType = null;
            while (transforms.Count > 0)
            {
                var tempNode = transforms.Pop();
                var transformer = (BaseTransformer)tempNode.Tag;
                lastOutputType = transformer.OutputType;
                value = transformer.Transform(value);
                if (value == null)
                {
                    transformErrorEncountered(tempNode);
                    treeView1.Refresh();
                    return;
                }
                // it's all good
                markNode(tempNode, nodeStatus.Ok);

            }

            UpdateTransformerTreeviews(false);
            if (theNode.IsSelected)
            {
                textBoxInputOutput.Text = TransformerHelper.DefaultTransform(lastOutputType, value);
                if (lastOutputType == typeof(String))
                    setStatusBarStringLength(((String)value).Length);
                else
                    setStatusBarByteLength(((Byte[])value).Length);
            }
        }

        private void transformErrorEncountered(TreeNode tempNode)
        {
            //Something went wrong on this node. Better update the GUI.
            var transformer = (BaseTransformer)tempNode.Tag;
            var SelectedNode = treeView1.SelectedNode;
            if (tempNode.IsSelected)
            {
                textBoxInputOutput.Text = "Error: " + transformer.LastError;
                setStatusBarError();
                treeViewStringTransformers.Enabled = false;
                treeViewByteTransformers.Enabled = false;
            }
            else if (NodeIsDecendant(tempNode, SelectedNode))
            {
                textBoxInputOutput.Text = "Error: A parent took an arrow to the knee.";
                setStatusBarError();
                treeViewStringTransformers.Enabled = false;
                treeViewByteTransformers.Enabled = false;
            }

            markNode(tempNode, nodeStatus.Error);

            foreach (TreeNode node in tempNode.Nodes)
            {
                flagWarningOnNodeAndChildren(node);
            }

        }

        private bool NodeIsDecendant(TreeNode AncestorCandidate, TreeNode DescendantCandidate)
        {
            var parent = DescendantCandidate.Parent;
            while (parent != null)
            {
                if (parent.Equals(AncestorCandidate)) return true;
                parent = parent.Parent;
            }
            return false;
        }

        private void flagWarningOnNodeAndChildren(TreeNode node)
        {
            markNode(node, nodeStatus.Warning);
            foreach (TreeNode childNode in node.Nodes)
                flagWarningOnNodeAndChildren(childNode);
        }

        private void UpdateTransformerTreeviews(bool setFocus)
        {
            var currentOutputType = getSelectedNodeOutputType();

            if (currentOutputType.Equals(typeof(String)))
            {
                treeViewStringTransformers.Enabled = true;
                treeViewByteTransformers.Enabled = false;
                if (setFocus) treeViewStringTransformers.Focus();
            }
            else
            {
                treeViewStringTransformers.Enabled = false;
                treeViewByteTransformers.Enabled = true;
                if(setFocus) treeViewByteTransformers.Focus();
            }
        }

        private void initTransformerListboxes()
        {
            var currentOutputType = getSelectedNodeOutputType();

            var stringTransforms = getTransformerListboxItems(typeof(String));
            var byteTransforms = getTransformerListboxItems(typeof(Byte[]));

            PopulateTreeviewFromTransformerList(treeViewStringTransformers, stringTransforms);
            PopulateTreeviewFromTransformerList(treeViewByteTransformers, byteTransforms);

        }

        private void PopulateTreeviewFromTransformerList(TreeView tv, List<BaseTransformer> transformerList)
        {

            TransformUtil.TransformCategory lastCategory = (TransformUtil.TransformCategory)Enum.GetValues(typeof(TransformUtil.TransformCategory)).GetValue(0);

            var commonNode = GetCategoryNode("Common");
            tv.Nodes.Add(commonNode);
            TreeNode categoryNode = GetCategoryNode(TransformUtil.GetFriendlyName(lastCategory));
            var firstCategoryNode = categoryNode;
            tv.Nodes.Add(categoryNode);
            foreach (var transformer in transformerList)
            {
                if (!lastCategory.Equals(transformer.MyCategory))
                {
                    if (categoryNode.Nodes.Count == 0)
                    {
                        tv.Nodes.Remove(categoryNode);
                        firstCategoryNode = null;
                    }

                    lastCategory = transformer.MyCategory;
                    categoryNode = GetCategoryNode(TransformUtil.GetFriendlyName(transformer.MyCategory));
                    tv.Nodes.Add(categoryNode);
                    if (firstCategoryNode == null) firstCategoryNode = categoryNode;
                }
                var node = new TreeNode()
                {
                    Text = transformer.NameForMenu,
                    Tag = transformer,
                    BackColor = Color.White
                };
                if (transformer.IsCommonTransformer)
                    commonNode.Nodes.Insert(tv.Nodes.IndexOf(firstCategoryNode), node);
                else
                    categoryNode.Nodes.Add(node);
            }

            tv.ExpandAll();
            tv.Nodes[0].EnsureVisible();
        }

        private TreeNode GetCategoryNode(String nodeText)
        {
            var node = new TreeNode()
            {
                Text = nodeText + "   ",
                NodeFont = new Font(treeView1.Font, FontStyle.Bold),
                BackColor = Color.White
            };
            return node;
        }

        private Type getSelectedNodeOutputType()
        {
            var currentNode = treeView1.SelectedNode;
            var currentOutputType = (currentNode.Level == 0 ? currentNode.Tag.GetType() : ((BaseTransformer)currentNode.Tag).OutputType);
            return currentOutputType;
        }

        List<BaseTransformer> getTransformerListboxItems(Type t)
        {
            var theTransformers = new List<BaseTransformer>();
            theTransformers.AddRange(from BaseTransformer bt in TransformerHelper.GetTransformers()
                                     where bt.InputType.Equals(t)
                                     select bt);
            return theTransformers;
        }


        private void textBoxInputOutput_TextChanged(object sender, EventArgs e)
        {
            if (treeView1.SelectedNode.Level > 0 || !(treeView1.SelectedNode.Tag is string))
                return;

            var enteredText = textBoxInputOutput.Text;
            var nodeText = (enteredText.Length > nodeTextLength ? enteredText.Substring(0, nodeTextLength) + "..." : enteredText);

            setStatusBarStringLength(enteredText.Length);

            treeView1.SelectedNode.Text = "Input: " + nodeText;
            treeView1.SelectedNode.Tag = enteredText;
            if (treeView1.SelectedNode.Nodes.Count > 0)
                updateDescendingNodesStatuses(treeView1.SelectedNode);
        }

        private void updateDescendingNodesStatuses(TreeNode treeNode)
        {
            if (treeNode.Nodes.Count == 0)
            {
                recalculateOutput(treeNode);
                return;
            }
            foreach (TreeNode childNode in treeNode.Nodes)
                updateDescendingNodesStatuses(childNode);
        }

        private void TreeViewTransformers_KeyDown(object sender, KeyEventArgs e)
        {

            if (e.Alt && (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up))
            {
                var tree = (TreeView)sender;
                var parent = tree.SelectedNode;
                while (parent.Parent != null)
                    parent = parent.Parent;
                if (e.KeyCode == Keys.Down && parent.NextNode != null)
                    tree.SelectedNode = parent.NextNode;
                else if (e.KeyCode == Keys.Up && parent.PrevNode != null)
                    tree.SelectedNode = parent.PrevNode.FirstNode;
                return;
            }

            if (!(e.KeyCode == Keys.Enter))
                return;
            var transformerListBoxKeyedDown = (TreeView)sender;
            var selectedTransformer = (BaseTransformer)transformerListBoxKeyedDown.SelectedNode.Tag;
            var relevantType = getSelectedNodeOutputType();
            var selectedNode = treeView1.SelectedNode;

            if (selectedTransformer == null || !selectedTransformer.InputType.Equals(relevantType))
                return;

            AddTreeNodeFromTreeviewTransformer(selectedTransformer, (!e.Alt));
            //treeView1.SelectedNode = selectedNode;

            UpdateTransformerTreeviews(true);
            //treeViewStringTransformers.Enabled = false;
        }

        private void alertUserError(String title, String message)
        {
            MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private void helpToolStripButton_Click(object sender, EventArgs e)
        {
            using (var aboutBox = new AboutBoxTransformTool())
            {
                aboutBox.ShowDialog();
            }
        }

        private void newToolStripButton_Click(object sender, EventArgs e)
        {
            createAndPopulateNewInputNode();
        }

        private void createAndPopulateNewInputNode()
        {
            var newNode = treeView1.Nodes.Add("Input");
            newNode.Tag = textBoxInputOutput.SelectedText;
            treeView1.SelectedNode = newNode;
            textBoxInputOutput.Focus();
        }

        private void createAndPopulateNewInputNodeWithReverseTransforms()
        {
            var currentNode = treeView1.SelectedNode;
            var newInputText = (textBoxInputOutput.SelectionLength > 0 ? textBoxInputOutput.SelectedText : textBoxInputOutput.Text);

            if (currentNode.Level == 0)
            {
                var newNode = treeView1.Nodes.Add("Input");
                newNode.Tag = newInputText;
                treeView1.SelectedNode = newNode;
                textBoxInputOutput.Focus();
                return;
            }
            //Not an input node, stack up transformers
            var transformerList = new LinkedList<BaseTransformer>();
            var currentOutputType = ((BaseTransformer)currentNode.Tag).OutputType;
            if (!currentOutputType.Equals(typeof(string)))
            {
                var defaultTransform = TransformerHelper.GetDefaultTransformer(currentOutputType);


                if (defaultTransform != null)
                    transformerList.AddLast((BaseTransformer)Activator.CreateInstance(defaultTransform.ReverseTransformer));
            }

            while (currentNode.Level > 0)
            {
                var transformer = (BaseTransformer)currentNode.Tag;
                var reverse = TransformerHelper.GetReverseTransformer(transformer);
                if (reverse == null)
                {
                    setStatusBarReverseError(transformer.NameForMenu);
                    return;
                }
                transformerList.AddLast(reverse);
                currentNode = currentNode.Parent;
            }
            //Now build the transforms!
            treeView1.BeginUpdate();
            currentNode = treeView1.Nodes.Add("Input");
            currentNode.Tag = newInputText;

            var newInput = currentNode;
            foreach (var transformer in transformerList)
            {
                currentNode = currentNode.Nodes.Add(transformer.NameForMenu);
                currentNode.Tag = transformer;
            }
            treeView1.EndUpdate();

            treeView1.SelectedNode = currentNode;
            treeView1.SelectedNode = newInput;
            textBoxInputOutput.Focus();

        }

        private void markNode(TreeNode node, nodeStatus status)
        {

            var imageIndex = 1000;

            switch (status)
            {
                case nodeStatus.Ok:
                    imageIndex = 0;
                    break;
                case nodeStatus.Error:
                    imageIndex = 1;
                    break;
                case nodeStatus.Warning:
                    imageIndex = 2;
                    break;
            }
            node.SelectedImageIndex = imageIndex;
            node.ImageIndex = imageIndex;
        }

        private void TransformTool_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.N && e.Control)
            {
                createAndPopulateNewInputNode();
                formKeyPressWasHandled = true;
                return;
            }

            if (e.KeyCode == Keys.R && e.Control)
            {
                createAndPopulateNewInputNodeWithReverseTransforms();
                formKeyPressWasHandled = true;
                return;
            }

            if (e.KeyCode == Keys.O && e.Control)
            {
                createNodeFromFile();
                formKeyPressWasHandled = true;
                return;
            }

            if (e.KeyCode == Keys.S && e.Control)
            {
                saveSelectedNodeOutputToFile();
                formKeyPressWasHandled = true;
                return;
            }

            if (e.KeyCode == Keys.Right && e.Alt)
            {
                if (treeView1.Focused)
                {
                    if (treeViewStringTransformers.Enabled)
                        treeViewStringTransformers.Focus();
                    else if (treeViewByteTransformers.Enabled)
                        treeViewByteTransformers.Focus();
                }
                else if (treeViewStringTransformers.Focused || treeViewByteTransformers.Focused)
                    textBoxInputOutput.Focus();
                else if (textBoxInputOutput.Focused)
                    treeView1.Focus();

                formKeyPressWasHandled = true;
                return;
            }

            if (e.KeyCode == Keys.Left && e.Alt)
            {
                if (textBoxInputOutput.Focused)
                {
                    if (treeViewStringTransformers.Enabled)
                        treeViewStringTransformers.Focus();
                    else if (treeViewByteTransformers.Enabled)
                        treeViewByteTransformers.Focus();
                }
                else if (treeViewStringTransformers.Focused || treeViewByteTransformers.Focused)
                    treeView1.Focus();
                else if (treeView1.Focused)
                    textBoxInputOutput.Focus();

                formKeyPressWasHandled = true;
                return;
            }

        }

        private void TransformTool_KeyPress(object sender, KeyPressEventArgs e)
        {
            e.Handled = formKeyPressWasHandled;
            formKeyPressWasHandled = false;
        }

        void setStatusBarStringLength(int len)
        {
            toolStripStatusLabel1.Text = "Length: " + len + " characters.";
        }

        void setStatusBarByteLength(int len)
        {
            toolStripStatusLabel1.Text = "Length: " + len + " bytes.";
        }

        void setStatusBarError()
        {
            toolStripStatusLabel1.Text = "An error occured.";
        }

        void setStatusBarReverseError(string transformerName)
        {
            toolStripStatusLabel1.Text = "Error: There's no reverse transform for " + transformerName;
        }

        void setStatusBarFileSaveError()
        {
            toolStripStatusLabel1.Text = "Error: The output must be bytes, apply a transform that yields bytes before saving (such as one of the text encodings).";
        }

        //Prevent manual changes to textbox if this is not an input node
        private void textBoxInputOutput_KeyPress(object sender, KeyPressEventArgs e)
        {
            e.Handled = textBoxKeyPressWasHandled;
            textBoxKeyPressWasHandled = false;
            if (selectedNodeIsUnEditable())
                e.Handled = true;

        }

        private void textBoxInputOutput_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.A && e.Control)
            {
                textBoxInputOutput.SelectAll();
                textBoxKeyPressWasHandled = true;
            }

            if (e.KeyCode == Keys.C && e.Control)
            {
                textBoxInputOutput.Copy();
                textBoxKeyPressWasHandled = true;
            }

            if (e.KeyCode == Keys.Delete && selectedNodeIsUnEditable())
            {
                e.Handled = true;
                textBoxKeyPressWasHandled = true;
            }
        }

        //top nodes with bytes, and any child node should not be editable.  
        private bool selectedNodeIsUnEditable()
        {
            return treeView1.SelectedNode.Level != 0 || treeView1.SelectedNode.Tag is byte[];
        }

        private void textBoxInputOutput_KeyUp(object sender, KeyEventArgs e)
        {
            UpdateTextboxCursorPositionBySelection();
        }

        private void UpdateTextboxCursorPositionBySelection()
        {
            var selStart = textBoxInputOutput.SelectionStart;
            if (textBoxInputOutput.SelectionLength > 0) selStart += textBoxInputOutput.SelectionLength;
            UpdateTextboxCursorPosition(selStart);
        }
        private void UpdateTextboxCursorPosition(int charIndex)
        {
            var subString = textBoxInputOutput.Text.Substring(0, charIndex);
            var strings = subString.Split(new string[] { Environment.NewLine }, int.MaxValue, StringSplitOptions.None);
            var lineNo = strings.Length - 1;
            var charNumOnLine = strings.Last().Length;
            toolStripStatusLabelPosition.Text = String.Format("Pos: {0,4:D},{1,-4:D} ", lineNo, charNumOnLine);
            UpdateTextboxSelectedLabel();
        }

        private void ResetTextboxCursorPosition()
        {
            toolStripStatusLabelPosition.Text = String.Format("Pos: {0,4:D},{1,-4:D} ", 0, 0);
            UpdateTextboxSelectedLabel();
        }

        private void UpdateTextboxSelectedLabel()
        {
            var selected = textBoxInputOutput.SelectionLength;
            toolStripStatusLabelSelected.Text = (selected == 0 ? "" : String.Format("Selected: {0,4:D} chars", selected));

        }

        private void openToolStripButton_Click(object sender, EventArgs e)
        {
            createNodeFromFile();
        }

        private void createNodeFromFile()
        {
            var openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "Text Documents (*.txt)|*.txt|All Files (*.*)|*.*";

            if (openFileDialog.ShowDialog() != DialogResult.OK)
                return;

            var fileName = openFileDialog.SafeFileName;

            var openFileStream = openFileDialog.OpenFile();

            if (openFileStream == null) return;

            var buffer = new byte[1024];
            var read = 0;
            byte[] entireFile;
            using (var ms = new MemoryStream())
            {
                using (openFileStream)
                {

                    read = openFileStream.Read(buffer, 0, buffer.Length);
                    while (read > 0)
                    {
                        ms.Write(buffer, 0, read);
                        read = openFileStream.Read(buffer, 0, buffer.Length);
                    }
                }
                entireFile = ms.ToArray();
            }

            var newNode = treeView1.Nodes.Add(fileName);
            newNode.Tag = entireFile;
            treeView1.SelectedNode = newNode;
        }

        private void saveToolStripButton_Click(object sender, EventArgs e)
        {
            saveSelectedNodeOutputToFile();
        }

        void saveSelectedNodeOutputToFile()
        {

            var saveFileDialog = new SaveFileDialog();
            saveFileDialog.Filter = "Text Documents (*.txt)|*.txt|All Files (*.*)|*.*";
            if (saveFileDialog.ShowDialog() != DialogResult.OK)
                return;

            byte[] output = new byte[] { 0x00 };
            if (treeView1.SelectedNode.Level == 0)
            {
                if (treeView1.SelectedNode.Tag is byte[])
                    output = (byte[])treeView1.SelectedNode.Tag;
                else if (treeView1.SelectedNode.Tag is string)
                    output = (byte[])new UTF8EncodeTransformer().Transform(treeView1.SelectedNode.Tag);
            }
            else
            {
                BaseTransformer nodeTransform = (BaseTransformer)treeView1.SelectedNode.Tag;
                if (nodeTransform.OutputType.Equals(typeof(string)))
                    output = (byte[])new UTF8EncodeTransformer().Transform(textBoxInputOutput.Text);
                else
                {
                    BaseTransformer toByteTransformer = (BaseTransformer)Activator.CreateInstance(TransformerHelper.GetDefaultTransformer(nodeTransform.OutputType).ReverseTransformer);
                    output = (byte[])toByteTransformer.Transform(textBoxInputOutput.Text);
                }
            }

            var saveFileStream = saveFileDialog.OpenFile();

            using (saveFileStream)
            {
                saveFileStream.Write(output, 0, output.Length);
                saveFileStream.Close();
            }
        }

        private void TransformTool_Shown(object sender, EventArgs e)
        {
            this.textBoxInputOutput.Focus();
        }

        private void textBoxInputOutput_DoubleClick(object sender, EventArgs e)
        {
            //UpdateTextboxCursorPositionByMouse(((MouseEventArgs)e).Location);
            UpdateTextboxCursorPositionBySelection();
        }

        private void UpdateTextboxCursorPositionByMouse(Point p)
        {
            var charIndex = textBoxInputOutput.GetCharIndexFromPosition(p);
            UpdateTextboxCursorPosition(charIndex);
        }

        private void textBoxInputOutput_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                UpdateTextboxCursorPositionByMouse(e.Location);
            }
        }

        private void textBoxInputOutput_MouseDown(object sender, MouseEventArgs e)
        {
            UpdateTextboxCursorPositionBySelection();//((MouseEventArgs)e).Location);
        }


    }
}
